﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Utilities
{
    [XmlRoot("user")]
    public class User
    {
        [XmlElement("name")]
        public string Name { get; set; }
        [XmlElement("email")]
        public string Email { get; set; }
        [XmlElement("address")]
        public string Address { get; set; }
        [XmlElement("birth", DataType = "date")]
        public DateTime Birth { get; set; }
        [XmlElement("phone")]
        public string Phone { get; set; }
        [XmlElement("pass")]
        public string PassHash { get; set; }
        [XmlElement("salt")]
        public string Salt { get; set; }
        [XmlElement("public")]
        public string RSAPublicKeyXmlString { get; set; }
        [XmlElement("pair")]
        public string RSAPairKeyXmlString { get; set; }

        public User() { }

        public User(string name, string email, string address, string phone, DateTime birth, string pass, int rsaKeySize)
        {
            this.Name = name;
            this.Email = email;
            this.Address = address;
            this.Phone = phone;
            this.Birth = birth;

            RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(rsaKeySize);

            // public key
            string pubKey = rsaCsp.ToXmlString(false);
            // private + public key
            string pairKey = rsaCsp.ToXmlString(true);

            // Generate salt, size is 8 byte
            byte[] salt = new byte[8];
            RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
            rngCsp.GetBytes(salt);
            // store this salt
            this.Salt = Utility.FromBytesToHexString(salt);

            // get pass bytes
            byte[] passByte = Encoding.ASCII.GetBytes(pass);

            TripleDESCryptoServiceProvider tripleDesCsp = new TripleDESCryptoServiceProvider();

            // Create password
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(passByte, salt);
            byte[] passByteDerived = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tripleDesCsp.IV);

            byte[] passHashBytes = EncryptionProvider.Hash(passByteDerived, new SHA1Managed());
            byte[] pairKeyBytes = Encoding.ASCII.GetBytes(pairKey);
            byte[] pairKeyEncryptedBytes = EncryptionProvider.TripleDesEncrypt(pairKeyBytes, passHashBytes, 192);

            this.PassHash = Utility.FromBytesToHexString(passHashBytes);
            this.RSAPublicKeyXmlString = pubKey;
            this.RSAPairKeyXmlString = Utility.FromBytesToHexString(pairKeyEncryptedBytes);
        }

        public RSACryptoServiceProvider GetRSAKeyPair(bool withPrivateKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            if (!withPrivateKey)
            {
                rsa.FromXmlString(this.RSAPublicKeyXmlString);
            }
            else
            {
                byte[] encBytes = Utility.FromHexStringToBytes(this.RSAPairKeyXmlString);

                byte[] passHashBytes = Utility.FromHexStringToBytes(this.PassHash);
                byte[] decBytes = EncryptionProvider.TripleDesDecrypt(encBytes, passHashBytes, 192);
                string decString = Encoding.ASCII.GetString(decBytes);

                rsa.FromXmlString(decString);
            }
            return rsa;
        }

        public void UpdatePassphrase(string pass)
        {
            RSACryptoServiceProvider rsaCsp = this.GetRSAKeyPair(true);

            // public key
            string pubKey = rsaCsp.ToXmlString(false);
            // private + public key
            string pairKey = rsaCsp.ToXmlString(true);

            // Generate salt, size is 8 byte
            byte[] salt = Utility.FromHexStringToBytes(this.Salt);

            // get pass bytes
            byte[] passByte = Encoding.ASCII.GetBytes(pass);

            TripleDESCryptoServiceProvider tripleDesCsp = new TripleDESCryptoServiceProvider();

            // Create password
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(passByte, salt);
            byte[] passByteDerived = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tripleDesCsp.IV);

            byte[] passHashBytes = EncryptionProvider.Hash(passByteDerived, new SHA1Managed());
            byte[] pairKeyBytes = Encoding.ASCII.GetBytes(pairKey);
            byte[] pairKeyEncryptedBytes = EncryptionProvider.TripleDesEncrypt(pairKeyBytes, passHashBytes, 192);

            this.PassHash = Utility.FromBytesToHexString(passHashBytes);
            this.RSAPublicKeyXmlString = pubKey;
            this.RSAPairKeyXmlString = Utility.FromBytesToHexString(pairKeyEncryptedBytes);
        }

        public bool CheckUser(string email, string pass)
        {

            if (email.Equals(this.Email))
            {
                // check passphrase

                byte[] salt = Utility.FromHexStringToBytes(this.Salt);
                byte[] inputBytes = Encoding.ASCII.GetBytes(pass);

                TripleDESCryptoServiceProvider tripleDesCsp = new TripleDESCryptoServiceProvider();

                // Create password
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(inputBytes, salt);
                byte[] passByteDerived = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, tripleDesCsp.IV);

                byte[] inputHashBytes = EncryptionProvider.Hash(passByteDerived, new SHA1Managed());
                string inputHash = Utility.FromBytesToHexString(inputHashBytes);

                return (inputHash.Equals(this.PassHash));
            }

            return false;
        }

        public override string ToString()
        {
            return this.Name;
        }
    }
}
